/**
 * class DataManager
 * 
 * DataManager provides functionality to action classes to perform actions that are required
 * abstracts the file handling and data storage from the action classes
 * allows us to easily modify the data storage without disturbing higher level functionality
 * 
 * current functionality-
 * expected functionality-
 * 
 * 
 * @author TeamJ3
 */

package storage;
import googlecalendar.GoogleCalendar;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

import core.Log;

import data.Event;
import data.EventDateTime;
import data.EventList;
import data.ListCollection;


public class DataManager
{
	public static int MAX_NUM_OF_LISTS = 20;
	private static ListCollection eventListCollection = new ListCollection();
	private static String currentListName = "default";
	private static FileDatabase quickDatabase = null;
	private static ArrayList<UpdateListener> updateListenerList = new ArrayList<UpdateListener>();
	private static HashMap<String, Event> eventHashMap = new HashMap<String, Event>();
	private static GoogleCalendar googleCalendar; 

	public DataManager() {

	}

	/**
	 * 
	 * @return String containing the name of the current List being used
	 */
	public static String getCurrentListName() {
		Log.debug("DM: Entering the getCurrentListName function");
		return currentListName;
	}

	/**
	 * function sets the name of the current list to the name specified
	 * @param currentListName
	 */
	public static void setCurrentListName(String currentListName) {
		Log.debug("DM: Entering the setCurrentListName function");
		DataManager.currentListName = currentListName;
	}

	/**
	 * 
	 * @param eventToBeAdded
	 * @return boolean- true if event is successfully added, false otherwise
	 */
	public static boolean add(Event eventToBeAdded) {
		Log.debug("DM: Entering the add function with one param");
		return add(eventToBeAdded, eventToBeAdded.getList());
	}

	/**
	 * 
	 * @param eventToBeAdded
	 * @param listName
	 * @return boolean- true if event is successfully added to specified list, false otherwise
	 */
	public static boolean add(Event eventToBeAdded, String listName) {
		Log.debug("DM: Entering the add function with 2 params");
		if (listName == null || listName.trim().equals("")) {
			eventToBeAdded.setList(currentListName);
			listName = currentListName;
		}

		String[] namesOfLists = getListNames();
		for(String nameOfList : namesOfLists) {
			if (eventToBeAdded.getList().toLowerCase().equals(nameOfList.toLowerCase())) {
				eventToBeAdded.setList(nameOfList);
			}
		}
		eventListCollection.addEvent(listName, eventToBeAdded);
		String eventId = eventToBeAdded.getId();
		if (eventId == null) {
			Log.debug("DM: In add function. Generating unique Id for event because Id was previously null");
			eventId = generateUniqueId(eventToBeAdded);
			eventToBeAdded.setId(eventId);
		}

		eventHashMap.put(eventId, eventToBeAdded);

		return saveLists(null, eventToBeAdded);
	}

	/**
	 * 
	 * @param eventToGetId
	 * @return A string containing the Id of the event passed as param
	 */
	private static String generateUniqueId(Event eventToGetId) {
		Log.debug("DM: Entering the generateUniqueId function");
		String eventId = "__$!"+(new Random().nextInt(Integer.MAX_VALUE));
		eventId += "!$__";
		if (eventHashMap.containsKey(eventId)) {
			return generateUniqueId(eventToGetId);
		}
		return eventId;
	}

	/**
	 * 
	 * @return boolean- true if lists are loaded into live storage, false otherwise
	 */
	public static boolean loadLists() {
		Log.debug("DM: Entering the loadLists function");
		quickDatabase = new FileDatabase();
		String[] listNames = getListNames();
		String eventId = null;
		if (listNames.length == 0) {
			EventList eventList = new EventList();
			eventListCollection.addList("default", eventList);
			quickDatabase.writeEventList(eventList);
			return true;
		}

		for (String listName : listNames) {
			if (listName == null || listName.trim().equals("")) {
				continue;
			}
			eventListCollection.addList(listName, quickDatabase.readList(listName));
		}

		for (EventList eventList : eventListCollection.getLists()) {
			for (Event e : eventList.getEvents()) {
				if (e.getStart() == null) {
					e.setStart(new EventDateTime());
				}
				eventId = e.getId();
				if (eventId == null) {
					eventId = generateUniqueId(e);
					e.setId(eventId);
				}
				eventHashMap.put(eventId, e);
			}
		}
		return true;
	}

	/**
	 * 
	 * @return String array containing names of all lists currently in storage
	 */
	public static String[] getListNames() {
		Log.debug("DM: Entering the getListNames function");
		String[] namesOfLists = quickDatabase.getListNames();
		return namesOfLists;
	}

	/**
	 * 
	 * @param oldEvent
	 * @param newEvent
	 * @return true if changes in list are updated in permanent storage, false otherwise
	 */
	private static boolean saveLists(Event oldEvent, Event newEvent) {
		Log.debug("DM: Entering the saveLists function");
		EventList[] eventListArray = eventListCollection.getLists();
		boolean flag = true;

		for (EventList eventList : eventListArray) {
			if(!quickDatabase.writeEventList(eventList)) {
				Log.warn("DM: In savelists function. List was not written to the file");
				flag = false;
			}
		}
		
		fireStateChanged(oldEvent, newEvent);
		return flag;
	}

	/**
	 * 
	 * @param oldEvent
	 * @param newEvent
	 */
	private static void fireStateChanged(Event oldEvent, Event newEvent) {
		Log.debug("DM: Entering the fireStateChanged function");
		for (UpdateListener listener : updateListenerList) {
			listener.updated(oldEvent, newEvent);
		}
	}

	/**
	 * 
	 * @param eventToRemove
	 * @return boolean- true if event is successfully removed from database, false if otherwise
	 */
	public static boolean remove(Event eventToRemove) {
		Log.debug("DM: Entering the remove function with 1 param");
		return remove(eventToRemove, eventToRemove.getList());
	}

	/**
	 * 
	 * @param eventToRemove
	 * @param listName
	 * @return true if event is successfully removed from specified list in database, false if otherwise
	 */
	public static boolean remove(Event eventToRemove, String listName) {
		Log.debug("DM: Entering the remove function with 2 params");
		if(listName == null) {
			listName = "default";
		}
		eventListCollection.removeEventFromList(eventToRemove, listName);
		return saveLists(eventToRemove, null);
	}
	
	/**
	 * 
	 * @param listName
	 * @return Array of all events in a specified list
	 */
	public static Event[] getAllEventsInList( String listName) {
		Log.debug("DM: Entering the getAllEventsInList function");
		ArrayList<Event> allEventsInList = eventListCollection.getAllEventsInList(listName);
		Event[] arrayOfEventsInList = (Event[]) allEventsInList.toArray();
		return arrayOfEventsInList;

	}

	/**
	 * 
	 * @return event array of ALL events currently in database
	 */
	public static Event[] getAllEventsInStorage() {
		Log.debug("DM: Entering the getAllEventsInStorage function");
		Event[] allEventsInStorage = null;
		ArrayList<Event> allEventsInList = null;
		String[] namesOfLists =  getListNames();

		for(int i=0; i<namesOfLists.length; i++ )  {
			allEventsInList = eventListCollection.getAllEventsInList(namesOfLists[i]);
			allEventsInStorage = allEventsInList.toArray(new Event[allEventsInList.size()]);
		}
		return allEventsInStorage;
	}

	/**
	 * 
	 * @param oldEvent
	 * @param newEvent
	 * @return boolean- true if event is replaced in database, false otherwise
	 */
	public static boolean replace(Event oldEvent, Event newEvent) {
		Log.debug("DM: Entering the replace function with 2 params");
		if(replace(oldEvent, newEvent, oldEvent.getList())) {
			Log.debug("DM: In replace function. Event was successfully replaced");
			return true;
		}
		return false;
	}

	/**
	 * 
	 * @param oldEvent
	 * @param newEvent
	 * @param listName
	 * @return boolean- true if event is replaced in database, false otherwise
	 */
	public static boolean replace(Event oldEvent, Event newEvent,
			String listName) {
		Log.debug("DM: Entering the replace function with 3 params");
		Event[] oldEvents = new Event[1];
		Event[] newEvents = new Event[1];
		oldEvents[0] = oldEvent;
		newEvents[0] = newEvent;
		
		boolean replaced = replaceArray(oldEvents, newEvents, listName);
		return replaced;
	}
	
	/**
	 * 
	 * @param oldEvents
	 * @param newEvents
	 * @param listName
	 * @return boolean- true if all events in the array are successfully replaced, false if even one fails
	 */
	public static boolean replaceArray(Event[] oldEvents, Event[] newEvents, String listName) {
		Log.debug("DM: Entering the replace array function");
		boolean replacedAll = false;
		if(oldEvents.length != newEvents.length) {
			Log.error("DM: number of new events not equal to old events in replace call");
			return false;
		}
		
		for(int i = 0; i<oldEvents.length; i++) {
			if(remove(oldEvents[i])) {
				if(add(newEvents[i] , listName)) {
					replacedAll = true;
				}
			}
		}
		
		return replacedAll;
	}

	/**
	 * 
	 * @param listName
	 * @return String array of names of all events in a specified list
	 */
	public static String[] getEventsInList(String listName) {
		Log.debug("DM: Entering the getEventsInList function");
		ArrayList<Event> list = eventListCollection.get(listName).getEvents();
		String[] listOfEvents = new String[list.size()];
		int pos = 0;
		for (Event event : list) {
			listOfEvents[pos++] = event.getName();
		}
		return listOfEvents;
	}

	/**
	 * 
	 * @param listener
	 */
	public static void addUpdateListener(UpdateListener listener) {
		Log.debug("DM: Entering the addUpdateListener function");
		updateListenerList.add(listener);
	}

	/**
	 * 
	 * @param id
	 * @return event object related to the id passed as param
	 */
	public static Event getEventById(String id) {
		Log.debug("DM: Entering the getEventbyId function");
		return eventHashMap.get(id);
	}
	
	/**
	 * 
	 * @param gcal
	 */
	public static void setGoogleCalendar(GoogleCalendar gcal) {
		Log.debug("DM: Entering the setGoogleCalendar function");
		googleCalendar = gcal;
	}
	
	/**
	 * 
	 * @return GoogleCalendar object
	 */
	public static GoogleCalendar getGoogleCalendar() {
		Log.debug("DM: Entering the getGoogleCalendar function");
		return googleCalendar;
	}
	
	/**
	 * 
	 */
	public static void exportToTxt() {
		Log.debug("DM: Entering the exportToTxt function");
		if(!quickDatabase.clearOldText()) {
			Log.warn("DM: Old File was not cleared in function exportToTxt. function in FileDatabase returned false");
			return;
		}
		EventList[] eventListArray = eventListCollection.getLists();

		for (EventList eventList : eventListArray) {
			ArrayList<Event> listOfEvents = new ArrayList<Event>();
			listOfEvents = eventList.getEvents();
			StringBuffer lines = new StringBuffer("");
			for(int i = 0; i < listOfEvents.size(); i++) {
				lines.append(listOfEvents.get(i)).append("\n");
			}
			quickDatabase.exportToText(lines.toString());

		}
	}
	
}
